package com.example.certificate.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.Response;
import java.util.Arrays;

/**
 * desc Aop执行顺序
 * 1、先执行 @Around 原方法之前的事件
 * 2、执行 @Before
 * 3、执行 @Around 中的原方法
 * 4、执行 @Around 中原方法之后的事件
 * 5、执行 @After 事件
 * 6、执行 @AfterReturning 返回事件
 *
 * desc Aop发生错误后执行顺序
 * 1、先执行 @Around 原方法之前的事件
 * 2、执行 @Before 方法
 * 3、执行 @Around 中的原方法
 * 4、执行 @After 事件
 * 5、执行 @AfterThrowing 爆出异常
 */
@Component
@Aspect
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    // 第一个*代表返回类型不限
    // 第二个*代表所有类
    // 第三个*代表所有方法
    // (..) 代表参数不限
    @Pointcut("execution(public * com.example.certificate.controller.*.*(..))")
    public void pointCut(){};


    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("<=====================================================");
        logger.info("请求来源： =》" + request.getRemoteAddr());
        logger.info("请求URL：" + request.getRequestURL().toString());
        logger.info("请求方式：" + request.getMethod());
        logger.info("响应方法：" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("请求参数：" + Arrays.toString(joinPoint.getArgs()));
        logger.info("------------------------------------------------------");
        startTime.set(System.currentTimeMillis());
    }

    @After("within(com.example.certificate.controller.*Controller)")
    public void after(){
        logger.info("<=====================================================");
        logger.info("方法执行结束");
        logger.info("------------------------------------------------------");
    }

    //环绕通知
    @Around("pointCut()")
    public Object logAround (ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = joinPoint.proceed();
        return obj;
    }



    @AfterReturning(pointcut="pointCut()",returning = "result")
    public void afterRunning(Response result){
       if(startTime.get() == null){
            startTime.set(System.currentTimeMillis());
        }
        logger.info("耗时（毫秒）：" +  (System.currentTimeMillis() - startTime.get()));
        logger.info("返回数据：{}", result);
        logger.info("==========================================>");
    }

    //当方法执行异常的时候,throwing是指定方法参数中的e来接收异常参数，可以查看发生的什么异常
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        System.out.println( "异常出现之后...afterThrowing: "+ e );
    }
}
